/*
 * Copyright (C) 2000-2008 Jens Nixdorf and various contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies of the Software, its documentation and marketing & publicity
 * materials, and acknowledgment shall be given in the documentation, materials
 * and software packages that this Software was used.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package org.mopore.jat.plugin;

import java.awt.MenuItem;
import java.awt.image.BufferedImage;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mopore.jat.core.messaging.Message;
import org.mopore.jat.core.messaging.MessageType;
import org.mopore.jat.util.Version;

public abstract class AbstractPlugin implements IJatPluginInternal {


	private static final String CAN_NOT_USE_DISPLAY_NAME = "Can not use display name";


	private final Log log = LogFactory.getLog(getClass().getName());
	
	
	private IPluginContext pluginContext;


	final public IPluginMetaInformation getMetaInformation() throws PluginException {
		return new PluginMetaInformationImpl( this );
	}


	final public Email getAuthorsEmail() throws PluginException {
		
		try {
			Email email = getAuthorsEmailImpl();
			return email;
		}
		catch( Exception e ) {
			throw new PluginException( "email of author is not valid", e );
		}
	}
	
	/**
	 * 
	 * Provid a valid email address of the author. An invalid email address
	 * will cause a PluginException
	 * @return
	 * @throws Exception 
	 */
	abstract protected Email getAuthorsEmailImpl() throws Exception;


	final public String getAuthorsName() throws PluginException {
		try {
			String name = getAuthorsNameImpl();
			notNullOrEmpty( name );
			return name;
		}
		catch( Exception e ) {
			throw new PluginException( "name of author is not valid", e );
		}
	}

	
	/**
	 * 
	 * Provid a valid name of the author. An invalid name will cause a 
	 * PluginException.
	 * @return
	 */
	abstract protected String getAuthorsNameImpl();
	
	
	
	final public Version getVersion() throws PluginException {
		try {
			Version version = getVersionImpl();
			return version;
		}
		catch( Exception e ) {
			throw new PluginException( "version is not valid", e );
		}
	}

	
	/**
	 * 
	 * Provide a valid Version. The version might be used by other plugins to
	 * @return
	 */
	abstract protected Version getVersionImpl();


	final public String getPluginName() throws PluginException {
		try {
			String name = getPluginNameImpl();
			notNullOrEmpty( name );
			return name;
		}
		catch( Exception e ) {
			throw new PluginException( "name of plugin is not valid", e );
		}
	}
	
	
	abstract protected String getPluginNameImpl();


	
	final public void onPluginLoad( IPluginContext pluginContext ) throws PluginException{
		this.pluginContext = pluginContext;
		onPluginLoadImpl( pluginContext );
	}
	
	/**
	 * Check for necessary conditions if needed or register listeners
	 * ITogoActionListener, IPluginFocusListener or IThemeChangeListener
	 * Note: Not all plugins might be availble via IPluginContext
	 * @throws PluginException
	 */
	abstract public void onPluginLoadImpl( IPluginContext pluginContext ) throws PluginException;
	
	
	final public void onPluginShutDown() throws PluginException{
		log.debug( "shut down will be initiated for this plugin" );
		onPluginShutDownImpl();
	}
	
	


	/**
	 * Will be called when plugin shutsdown normally
	 * @throws PluginException
	 */
	abstract public void onPluginShutDownImpl() throws PluginException;

	
	
	
	public PluginLoadState getLoadState() {
		return pluginContext.getPluginLoadState( getPluginNameImpl() );
	}
	
	
	
	private static void notNullOrEmpty( String string ) {
		
		if ( string == null ) {
			throw new NullPointerException();
		}
		if ( string.length() < 4 ) {
			throw new IllegalArgumentException();
		}
	}
	
	
	/**
	 * 
	 * You can implement a loop here.
	 * You will be informed about the shutdown by a call of 
	 * onPluginShutDownImpl. You are asked to react on this call.
	 * An infintive loop in this very method implemention will be
	 * interrupted otherwise if you do not stop it smothly.
	 * 
	 * @param pluginContext
	 */
	abstract public void runPlugin( IPluginContext pluginContext ) throws Exception;

	
	final public Class<Void> call() throws Exception {
		try {
			runPlugin( pluginContext );
		} catch ( Throwable t ) {
			String title = "Serious plugin problem";
			log.error( title, t );
			String messageText = "The following plugin crashed: " + getPluginName();
			Message message = new Message( title, messageText, MessageType.ERROR );
			pluginContext.showMessage( message );
		}
		return Void.TYPE;
	}
	
	
	
	
	final public String getDisplayName() throws PluginException {
		
		String displayName;
		try {
			displayName = getDisplayNameImpl();
			notNullOrEmpty( displayName );
		} catch (RuntimeException e) {
			log.error( CAN_NOT_USE_DISPLAY_NAME );
			throw new PluginException( CAN_NOT_USE_DISPLAY_NAME, e );
		}
		
		return displayName;
	}
	
	
	abstract public String getDisplayNameImpl();


	abstract public BufferedImage getPluginIcon();
	

	abstract public MenuItem[] getMenuFocusActions();


	abstract public MenuItem[] getMenuActions( MenuItem base );


	abstract public boolean isFocusable();
	
	
	public void doubleClicked( IJatPlugin plugin ) throws PluginException {
		log.debug( "doubleClicked" );
	}

	public void leftClicked( IJatPlugin plugin ) throws PluginException {
		log.debug( "leftClicked" );
	}

	public void onFocusGained( IJatPlugin plugin ) throws PluginException {
		log.debug( "onFocusGained" );
	}

	public void onFocusLost( IJatPlugin plugin ) throws PluginException {
		log.debug( "onFocusLost" );
	}
	
	
}
